package org.particlethink.service.listen.listener;

import kotlin.Pair;
import kotlin.Triple;
import org.particlethink.entry.DeliverStatus;
import org.particlethink.entry.PlanTyp;
import org.particlethink.mapper.*;
import org.particlethink.model.*;
import org.particlethink.service.listen.event.ConfirmDeliverEvent;
import org.particlethink.utils.ListUtilsKt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 更新批次计划入库率
 */
@Component
public class UpdateBatchPlanInventoryRateListener implements ApplicationListener<ConfirmDeliverEvent> {

    @Autowired
    private DeliverMapper deliverMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private BatchPlanMapper batchPlanMapper;

    @Autowired
    private MasterPlanMapper masterPlanMapper;

    @Autowired
    private AcceptanceMapper acceptanceMapper;

    @Autowired
    private PlanMaterialMapper planMaterialMapper;

    @Override
    public void onApplicationEvent(ConfirmDeliverEvent event) {
        Deliver deliver = deliverMapper.selectByPrimaryKey(event.getDeliveredId());
        Order order = orderMapper.selectByPrimaryKey(deliver.getOrderId());
        if (order != null && order.getBatchPlanId() != 0) {

            // 查出子订单所属的上层信息
            // todo 子订单信息是否需要冗余总计划/批次计划的信息以减少查询次数
            BatchPlan batchPlan = batchPlanMapper.selectByPrimaryKey(order.getBatchPlanId());

            List<BatchPlan> batchPlans = batchPlanMapper.selectAllByMasterPlanID(batchPlan.getMasterPlanId());
            List<Order> orders = orderMapper.selectByBatchPlanIdsAndStatuses(batchPlans.stream().map(BatchPlan::getId).collect(Collectors.toList()), null);
            List<Deliver> delivers = deliverMapper.selectByOrderIdsAndStatuses(orders.stream().map(Order::getId).collect(Collectors.toList()), Arrays.asList(DeliverStatus.UnChecked, DeliverStatus.UnConfirmed, DeliverStatus.Warehoused, DeliverStatus.Reconciled));


            Map<Integer, Integer> batchid2masterIdMap = ListUtilsKt.filterMapKV(batchPlans, batch -> {
                return new Triple<>(batch.getMasterPlanId() == null || batch.getMasterPlanId() == 0, batch.getId(), batch.getMasterPlanId());
            });

            Map<Integer, Integer> ordreId2batchPlanIdMap = ListUtilsKt.mapKV(orders, d -> {
                return new Pair<>(d.getId(), d.getBatchPlanId());
            });

            Map<Integer, Integer> deliverId2batchPlanIdMap = ListUtilsKt.filterMapKV(delivers, d -> {
                Integer batchPlanId = ordreId2batchPlanIdMap.get(d.getOrderId());
                return new Triple<>(batchPlanId == null || batchPlanId == 0, d.getId(), batchPlanId);
            });

            List<PlanMaterial> deliverPlanMaterials = planMaterialMapper.selectByPlanIDsAndTyp(delivers.stream().map(Deliver::getId).collect(Collectors.toList()), PlanTyp.Deliver);


            HashMap<String, CompleteDto> objectObjectHashMap = new HashMap<>();
            deliverPlanMaterials.forEach((deliverPlanMaterial) -> {
                Integer deliverId = deliverPlanMaterial.getPlanId();
                Integer batchPlanID = deliverId2batchPlanIdMap.get(deliverId);

                String key = batchPlanID + "_" + deliverPlanMaterial.getMaterialId();


                if (!objectObjectHashMap.containsKey(key)) {
                    objectObjectHashMap.put(key, new CompleteDto());
                }

                CompleteDto completeDto = objectObjectHashMap.get(key);
                completeDto.IncrCompleteNum(deliverPlanMaterial.getCompleteNum());
                completeDto.IncrActualNum(deliverPlanMaterial.getActualNum());

            });


            List<PlanMaterial> batchPlanMaterials = planMaterialMapper.selectByPlanIDsAndTyp(batchPlans.stream().map(BatchPlan::getId).collect(Collectors.toList()), PlanTyp.Batch);

            batchPlanMaterials = batchPlanMaterials.stream().filter((planMaterial) -> {
                return objectObjectHashMap.containsKey(planMaterial.getPlanId() + "_" + planMaterial.getMaterialId());
            }).map((PlanMaterial planMaterial) -> {
                CompleteDto completeDto = objectObjectHashMap.get(planMaterial.getPlanId() + "_" + planMaterial.getMaterialId());
                planMaterial.setCompleteNum(completeDto.getCompleteNum());
                planMaterial.setActualNum(completeDto.getActualNum());
                return planMaterial;
            }).collect(Collectors.toList());

            if (batchPlanMaterials.isEmpty()) {
                return;
            }

            planMaterialMapper.updateActualAndCompleteNum(batchPlanMaterials);


            List<PlanMaterial> notAllShipment = batchPlanMaterials.stream().filter((b) -> {
                return b.getOrderId().equals(deliver.getOrderId()) && (b.getExpectNum() - b.getActualNum() > 0);
            }).collect(Collectors.toList());

            boolean needUpdate = false;
            if (notAllShipment.isEmpty() && !order.getAllShipment()) {
                order.setAllShipment(true);
                needUpdate = true;
            }
            if (!notAllShipment.isEmpty() && order.getAllShipment()) {
                order.setAllShipment(false);
                needUpdate = true;
            }
            if (needUpdate) {
                orderMapper.updateByPrimaryKeySelective(order);
            }


            Map<String, Integer> masterPlanIDMaterialId2completeNumMap = batchPlanMaterials.stream().
                    filter((batchPlanMaterial) -> {
                        return batchPlanMaterial.getMaterialId() != null && batchPlanMaterial.getMaterialId() != 0;
                    })
                    .collect(Collectors.groupingBy((batchPlanMaterial) -> {
                        Integer batchPlanId = batchPlanMaterial.getPlanId();
                        Integer masterPlanId = batchid2masterIdMap.get(batchPlanId);

                        return masterPlanId + "_" + batchPlanMaterial.getMaterialId();
                    }, Collectors.summingInt(PlanMaterial::getCompleteNum)));

            MasterPlan masterPlan = masterPlanMapper.selectByPrimaryKey(batchPlan.getMasterPlanId());
            if (masterPlan != null) {
                List<PlanMaterial> masterPlanMaterials = planMaterialMapper.selectByPlanIDAndTyp(masterPlan.getId(), PlanTyp.Master);
                masterPlanMaterials = masterPlanMaterials.stream().map((planMaterial) -> {
                    planMaterial.setCompleteNum(masterPlanIDMaterialId2completeNumMap.get(planMaterial.getPlanId() + "_" + planMaterial.getMaterialId()));
                    return planMaterial;
                }).collect(Collectors.toList());

                planMaterialMapper.updateCompleteNum(masterPlanMaterials);
            }
        }
    }
}
